<!DOCTYPE html>
<html>
<head>
<title>InputEvent: cancelable</title>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<style>
div {
    width: 100px;
    height: 100px;
}
</style>
</head>
<body>
<div id="editable" contenteditable></div>
<div id="editable1" contenteditable>EditableText</div>
<div id="editable2" contenteditable></div>
<script>
function simulateDragDrop(dragElement, dropElement) {
    if (dragElement.select) {
        dragElement.select();
    } else {
        var selection = window.getSelection();
        selection.collapse(dragElement, 0);
        selection.extend(dragElement, 1);
    }

    eventSender.mouseMoveTo(dragElement.offsetLeft + dragElement.offsetWidth / 2,
                            dragElement.offsetTop + dragElement.offsetHeight / 2);
    eventSender.mouseDown();
    eventSender.leapForward(600);
    eventSender.mouseMoveTo(dropElement.offsetLeft + dropElement.offsetWidth / 2,
                            dropElement.offsetTop + dropElement.offsetHeight / 2);
    eventSender.mouseUp();
}

async_test(t => {
    assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
    assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
    assert_not_equals(internals, undefined, 'This test requires internals.');
    assert_not_equals(textInputController, undefined, 'This test requires textInputController.');

    const editable = document.getElementById('editable');
    const editable1 = document.getElementById('editable1');
    const editable2 = document.getElementById('editable2');
    const kNoncancelableInputTypes = [
        'insertCompositionText',
    ];

    let lastBeforeInputType = '';
    let doCancel = false;
    document.addEventListener('beforeinput', t.step_func(event => {
        lastBeforeInputType = event.inputType;
        assert_equals(kNoncancelableInputTypes.indexOf(event.inputType) === -1, event.cancelable);
        if (doCancel) event.preventDefault();
    }));

    function testKeyDown(key, modifiers, inputType) {
        lastBeforeInputType = '';
        doCancel = false;
        eventSender.keyDown(key, modifiers);
        assert_equals(inputType, lastBeforeInputType, `${modifiers.toString()}+${key} should produce input type: ${inputType}`);
    }

    function testCommand(command, inputType) {
        lastBeforeInputType = '';
        doCancel = false;
        testRunner.execCommand(command);
        assert_equals(inputType, lastBeforeInputType, `${command} should produce input type: ${inputType}`);
    }

    function sendCancel(key, inputType) {
        lastBeforeInputType = '';
        doCancel = true;
        eventSender.keyDown(key, []);
    }

    // Typing
    editable.focus();
    testKeyDown('a', [], 'insertText');
    testKeyDown('6', [], 'insertText');
    testKeyDown('l', ['shiftKey'], 'insertText');
    testKeyDown('w', ['shiftKey'], 'insertText');
    testKeyDown('Enter', [], 'insertParagraph');
    testKeyDown('Enter', ['shiftKey'], 'insertLineBreak');
    editable.innerText = '';
    testKeyDown('a', [], 'insertText');
    assert_equals(editable.innerText, 'a');
    sendCancel('b', 'insertText');
    assert_equals(editable.innerText, 'a');

    // Deletion
    const isMacOS = navigator.platform.indexOf('Mac') === 0;
    const deleteWordModifier = isMacOS ? 'altKey' : 'ctrlKey';
    editable.innerHTML = 'abc def</br>123 456';
    const selection = window.getSelection();
    selection.collapse(editable, 1); // End of first line.
    testKeyDown('Backspace', [], 'deleteContentBackward');
    testKeyDown('Delete', [], 'deleteContentForward');
    testKeyDown('Backspace', [deleteWordModifier], 'deleteWordBackward');
    testKeyDown('Delete', [deleteWordModifier], 'deleteWordForward');
    testCommand('deleteToBeginningOfLine', 'deleteSoftLineBackward');
    testCommand('deleteToEndOfLine', 'deleteSoftLineForward');
    testCommand('deleteToBeginningOfParagraph', 'deleteHardLineBackward');
    testCommand('deleteToEndOfParagraph', 'deleteHardLineForward');
    editable.innerText = '';
    testKeyDown('a', [], 'insertText');
    assert_equals(editable.innerText, 'a');
    sendCancel('Backspace', 'deleteContentBackward');
    assert_equals(editable.innerText, 'a');

    // Format
    editable.innerHTML = 'abc';
    selection.collapse(editable, 0);
    selection.extend(editable, 1);
    testCommand('bold', 'formatBold');
    testCommand('italic', 'formatItalic');
    testCommand('underline', 'formatUnderline');
    testCommand('strikeThrough', 'formatStrikeThrough');
    testCommand('superscript', 'formatSuperscript');
    testCommand('subscript', 'formatSubscript');

    // Cut and paste
    editable.innerHTML = 'abc';
    selection.collapse(editable, 0);
    selection.extend(editable, 1);
    testKeyDown('Cut', [], 'deleteByCut');
    testKeyDown('Paste', [], 'insertFromPaste');

    // Drag and drop
    simulateDragDrop(editable1, editable2);
    assert_equals('insertFromDrop', lastBeforeInputType);

    // Undo and redo
    testCommand('undo', 'historyUndo');
    testCommand('redo', 'historyRedo');

    // Spell-checker
    editable.innerHTML = 'appla';
    selection.collapse(editable, 0);
    selection.extend(editable, 1);
    internals.setMarker(document, selection.getRangeAt(0), 'Spelling');
    internals.replaceMisspelled(document, 'apple');
    assert_equals('insertReplacementText', lastBeforeInputType);

    // IME
    editable.innerHTML = 'wo';
    selection.collapse(editable, 0);
    selection.extend(editable, 1);
    textInputController.setComposition('word');
    assert_equals('insertCompositionText', lastBeforeInputType);

    t.done();
}, 'Text input and IME related input types are non-cancelable.');

</script>
</body>
</html>
